/**
 * Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jfinal.i18n;

import java.io.File;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Const;
import com.jfinal.core.Controller;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;

/**
 * I18nInterceptor is used to change the locale by request para,
 * and it is also switch the view or pass Res object to the view.
 * 
 * you can extends I18nInterceptor and override the getLocaleParaName() and getResName()
 * to customize configuration for your own i18n Interceptor
 */
public class I18nInterceptor implements Interceptor {
	
	private String localeParaName = "_locale";
	private String resName = "_res";
	private boolean isSwitchView = false;
	
	public I18nInterceptor() {
	}
	
	public I18nInterceptor(String localeParaName, String resName) {
		if (StrKit.isBlank(localeParaName)) {
			throw new IllegalArgumentException("localeParaName can not be blank.");
		}
		if (StrKit.isBlank(resName)) {
			throw new IllegalArgumentException("resName can not be blank.");
		}
		
		this.localeParaName = localeParaName;
		this.resName = resName;
	}
	
	public I18nInterceptor(String localeParaName, String resName, boolean isSwitchView) {
		this(localeParaName, resName);
		this.isSwitchView = isSwitchView;
	}
	
	public I18nInterceptor(boolean isSwitchView) {
		this.isSwitchView = isSwitchView;
	}
	
	/**
	 * Return the localeParaName, which is used as para name to get locale from the request para and the cookie.
	 */
	protected String getLocaleParaName() {
		return localeParaName;
	}
	
	/**
	 * Return the resName, which is used as attribute name to pass the Res object to the view.
	 */
	protected String getResName() {
		return resName;
	}
	
	/**
	 * Return the baseName, which is used as base name of the i18n resource file.
	 */
	protected String getBaseName() {
		return I18n.defaultBaseName;
	}
	
	/**
	 * 1: use the locale from request para if exists. change the locale write to the cookie
	 * 2: use the locale from cookie para if exists.
	 * 3: use the default locale
	 * 4: use setAttr(resName, resObject) pass Res object to the view.
	 */
	public void intercept(Invocation inv) {
		Controller c = inv.getController();
		// 没有render则先invoke
		if (c.getRender() == null) {
			inv.invoke();
		}
		String localeParaName = getLocaleParaName();
		// 从请求头获取
	    String locale = c.getPara(localeParaName);
		if (StrKit.isBlank(locale)) {
			locale = c.getAttr(localeParaName);
	        // 再从controller缓存获取
			if (StrKit.isBlank(locale)) {
	            locale = c.getLocale();
			}
		}
		if (StrKit.notBlank(locale)) {	// change locale, write cookie
			c.setCookie(localeParaName, locale, Const.DEFAULT_I18N_MAX_AGE_OF_COOKIE);
		}
		else {							// get locale from cookie and use the default locale if it is null
			// 从cookie里面获取
			locale = c.getCookie(localeParaName);
			if (StrKit.isBlank(locale))
				// 最后获取不到就取缺省的
				locale = I18n.defaultLocale;
		}
		// 存在render则后invoke
		if (c.getRender() != null) {
			inv.invoke();
		}
		
		if (isSwitchView) {
			switchView(locale, inv);
		}
		else {
			// 非切换view模式
			ResKit res = new ResKit(getBaseName(), locale);
//			Res res = I18n.use(getBaseName(), locale);
			c.setAttr(getResName(), res);
            c.setAttr(localeParaName, locale);
		}
	}
	
	/**
	 * 在有些 web 系统中，页面需要国际化的文本过多，并且 css 以及 html 也因为际化而大不相同，
	 * 对于这种应用场景先直接制做多套同名称的国际化视图，并将这些视图以 locale 为子目录分类存放，
	 * 最后使用本拦截器根据 locale 动态切换视图，而不必对视图中的文本逐个进行国际化切换，省时省力。
	 */
    public void switchView(String locale, Invocation inv) {
        Controller c = inv.getController();
        String localePath = Controller.getLocalePath(locale);
		//切换视图模式，使用扩展Controller.getLocalePath调用返回路径
		if (StrKit.isBlank(localePath)) {
        	System.err.println("localePath not exist: " + localePath);
			//改为使用locale作为多语言视图路径
			localePath = locale;
		}
		
        String viewPath = inv.getViewPath();
        String webappPath = c.getRequest().getServletContext().getRealPath("/");
		Render render = c.getRender();
//		if (render != null) {
//			String view = render.getView();
//			if (view != null) {
//				if (view.startsWith("/")) {
//					view = "/" + localePath + view;
//				} else {
//					view = localePath + "/" + view;
//				}
//				
//				render.setView(view);
//			}
//		}
        if (render != null) {
            String view = render.getView();
            if (view != null) {
                if (view.startsWith("/")) {
                    view = "/" + localePath + view;
                } else {
                    view = localePath + "/" + view;
                }
                // 检测是否存在对应的视图网页文件
                final String realViewFile;
                if (File.separator.equals("/")) {
                    realViewFile = webappPath + viewPath + view;
                }
                else {
                    realViewFile = webappPath + viewPath.replaceAll("/", File.separator + File.separator) + view.replaceAll("/", File.separator + File.separator);
                }
                if (new File(realViewFile).isFile()) {
                    render.setView(view);
                }
                else {
                	// 视图网页文件不存在的处理，支持混合模式
                	System.err.println("RealViewFile not exist: " + realViewFile);
                	// 走非isSwitchView模式
                	ResKit res = new ResKit(getBaseName(), locale);
                    c.setAttr(getResName(), res);
                }
            }
        }
	}
}